package core;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.sql.SQLException;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import models.features.BrianBrain;
import models.features.LoginMenu;
import models.features.Statistics;
import models.games.Game;
import org.jdesktop.animation.timing.Animator;
import org.jdesktop.animation.transitions.ScreenTransition;
import org.jdesktop.animation.transitions.TransitionTarget;
import views.features.ChronologyView;
import views.features.CreateAccountView;
import views.features.GameMenuView;
import views.features.InstructionView;
import views.features.LoginMenuView;
import views.features.MainMenuView;
import views.features.RecoveryPasswordView;
import views.features.StatisticsView;
import views.games.GameView;
import views.util.GameColors;
import controllers.features.CalendarPanelController;
import controllers.features.GameMenuController;
import controllers.features.LoginMenuController;
import controllers.features.MainMenuController;
import controllers.features.StatisticsController;
import core.constants.Constants;
import core.database.DataBaseManager;

public class GameLauncher extends JComponent implements TransitionTarget {

	public enum State {
		LOGIN, GAME_MENU, REGISTER, GAME, RECOVERY_PASSWORD, MAIN_MENU, INSTRUCTIONS, CHRONOLOGY, STATISTCS
	}

	private final static int HEIGHT = 680, WIDTH = 800;

	Animator animator = new Animator(600);
	ScreenTransition transition = new ScreenTransition(this, this, animator);
	private JPanel currentScreen = new JPanel();
	private State state = State.LOGIN;
	private Game currentModel;
	private ActionListener currentController;
	private User currentUser;
	private BrianBrain brianBrain;
	private static String nameString;

	private String oldPassword = Constants.debug ? Constants.DEFAULT_PASS() : "";
	private String currentUsername = Constants.debug ? Constants.DEFAULT_USER() : "";

	private ProgressBarPanel panel;
	private int currentLevel;
	private static JButton back = new JButton("Indietro");
	private static JPanel levelBar = new JPanel(new GridBagLayout());
	private static GridBagConstraints gbc = new GridBagConstraints();
	private JLabel userLabel = new JLabel("");

	private int prevH;
	private int prevW;

	public GameLauncher() {
		animator.setAcceleration(.2f); // Accelerate for first 20%
		animator.setDeceleration(.4f); // Decelerate for last 40%
	}

	/**
	 * Arrange the GUI for the initial search screen.
	 */
	private void setupLoginScreen() {
		LoginMenu menuModel = null;
		try {
			menuModel = new LoginMenu();
		} catch (SQLException e) {

			e.printStackTrace();
		} catch (IOException e) {

			e.printStackTrace();
		}
		currentScreen = new LoginMenuView(currentUsername, oldPassword);
		LoginMenuController menuController = new LoginMenuController(menuModel,(LoginMenuView) currentScreen, this); // currentPanel in questo momento è un LoginMenuView
		add(currentScreen);
		((LoginMenuView) currentScreen).checkFocusMain();
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
		levelBar.setBackground(Color.WHITE);
	}

	private void setupRecoveryScreen() {
		LoginMenu menuModel = null;
		try {
			menuModel = new LoginMenu();
		} catch (SQLException | IOException e) {
			System.out.println(e + " in gameLauncher");
			e.printStackTrace();
		}
		try {
			currentUsername = ((LoginMenuView) currentScreen)
					.getUserTextField();
			currentScreen = new RecoveryPasswordView(currentUsername,
					menuModel.getSecretQuestion(currentUsername));
		} catch (SQLException | IllegalStateException e) {
			System.out.println(e + " in GameLauncher -> setupRecoveryScreen()");
			e.printStackTrace();
		}
		LoginMenuController menuController = new LoginMenuController(menuModel,(RecoveryPasswordView) currentScreen, this); // currentPanel in questo momento è un LoginMenuView
		add(currentScreen);
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
	}

	private void setupRegisterScreen() {
		LoginMenu menuModel = null;
		currentScreen = new CreateAccountView();
		try {
			menuModel = new LoginMenu();
		} catch (SQLException | IOException e) {
			e.printStackTrace();
		}
		LoginMenuController menuController = new LoginMenuController(menuModel,(CreateAccountView) currentScreen, this); // currentPanel in questo momento è un LoginMenuView
		add(currentScreen);
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
	}

	private void setupMainMenuScreen() {
		MainMenuView view = MainMenuView.newInstance();
		currentScreen = view;
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
		add(currentScreen);
		MainMenuController m = MainMenuController.newInstance(view, this);
	}

	private void setupChronologyView() {
		currentScreen = ChronologyView.newInstance();
		CalendarPanelController cp = CalendarPanelController.newInstance(
				(ChronologyView) currentScreen, currentUser);
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
		add(currentScreen);
	}

	private void setupInstructionsView() {
		currentScreen = InstructionView.newInstance();
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
		add(currentScreen);
	}

	private void setupStatisticsView() {
		StatisticsView view = StatisticsView.newInstance();
		Statistics model = Statistics.newInstance(currentUser);
		currentScreen = view;
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
		add(currentScreen);
		StatisticsController controller = StatisticsController.newInstance(
				model, view);
	}

	/**
	 * Arrange the GUI for the results screen
	 */
	public void setupGameMenuScreen() {
		try {
			currentScreen = GameMenuView.newInstance(this);
		} catch (IOException e) {
			e.printStackTrace();
		}
		currentScreen.setBounds(0, 0, WIDTH, HEIGHT);
		GameMenuController gm = GameMenuController.newInstance(
				(GameMenuView) currentScreen, this);
		String p = brianBrain.parla();
		System.out.println("BRIAN :" + p);
		((GameMenuView) currentScreen).setBrianBrainSpeech(p);
		add(currentScreen);
		back.setEnabled(true);
	}

	public void setupNextScreen() {
		removeAll();
		System.out.println("STATO CORRENTE " + state);
		switch (state) {
		case LOGIN:
			setupLoginScreen();
			break;
		case MAIN_MENU:
			setupMainMenuScreen();
			break;
		case STATISTCS:
			setupStatisticsView();
			break;
		case CHRONOLOGY:
			setupChronologyView();
			break;
		case INSTRUCTIONS:
			setupInstructionsView();
			break;
		case GAME_MENU:
			setupGameMenuScreen();
			break;
		case REGISTER:
			setupRegisterScreen();
			break;
		case GAME:
			setupGameScreen();
			break;
		case RECOVERY_PASSWORD:
			setupRecoveryScreen();
		default:
			break;
		}
	}

	@Override
	public void setBounds(int x, int y, int w, int h) {
		super.setBounds(x, y, w, h);
		if (w != prevW || h != prevH) {
			// Setup GUI for current screen given new size of our container
			setupNextScreen();
			prevW = w;
			prevH = h;
		} else {
			if (currentScreen instanceof GameView) {
				((GameView) currentScreen).checkFocusMain();
			}
		}
	}

	public void setGame(Game newModel, GameView newView,ActionListener newController) {
		currentModel = newModel;
		currentModel.setCurrentUser(currentUser);
		currentScreen = newView;
		currentController = newController;
	}

	public void setupGameScreen() {
		back.setEnabled(false);
		currentScreen.setBounds(0, -30, WIDTH, HEIGHT);
		add(currentScreen);
		((GameView) currentScreen).checkFocusMain();
	}

	private static void createAndShowGUI() {
		JFrame frame = new JFrame("M-Health");
		frame.setBackground(Color.WHITE);
		Dimension dim = new Dimension(WIDTH, HEIGHT);
		frame.setSize(dim);
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				try {
					DataBaseManager.closeConnection(); //Interrompe la connesione con il database
				} catch (SQLException | NullPointerException e1){}
				System.exit(0); //Chiuda l'applicazione
			}
		});
		Dimension frameSize = frame.getSize();
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		frame.setLocation((screenSize.width - frameSize.width) / 2,
				(screenSize.height - frameSize.height) / 2);
		frame.setVisible(true);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setResizable(false);
		frame.setVisible(true);
		GameLauncher component = new GameLauncher();
		frame.setLayout(new BorderLayout());
		frame.add(component, BorderLayout.CENTER);

		levelBar.setPreferredSize(new Dimension(WIDTH, 45));
		frame.add(levelBar, BorderLayout.NORTH);

	}

	public void startTransition(State s) {
		state = s;
		transition.start();
		System.out.println("TRANSIZIONE A " + s);
		if (state == State.LOGIN || state == State.REGISTER
				|| state == State.RECOVERY_PASSWORD) {
			levelBar.setVisible(false);
		} else {
			levelBar.setVisible(true);
			levelBar.setBackground(GameColors.NERO_BARRA_SUPERIORE);
		}
	}

	public static void main(String[] args) {
		if (args != null && args.length > 0 && args[0] != null && args[0].equals("-b")) {
			Constants.setDebug(true);
		}
		Runnable doCreateAndShowGUI = new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		};
		SwingUtilities.invokeLater(doCreateAndShowGUI);
	}

	public void setOldPassword(String oldPassword) {
		this.oldPassword = oldPassword;

	}

	public void setUser(User currentUser) {
		this.currentUser = currentUser;
		brianBrain = new BrianBrain(currentUser);
	}

	public void setLevelBar(String user, int level, JPanel panel) {
		nameString = user;
		userLabel.setFont(new Font("Segoe UI", Font.BOLD, 20));
		userLabel.setForeground(Color.WHITE);
		userLabel.setText((nameString.toUpperCase()));
		levelBar.setBackground(new Color(48, 48, 48));
		gbc.gridx = 0;
		gbc.gridy = 0;
		levelBar.add(userLabel, gbc);
		gbc.gridx = 1;
		levelBar.add(panel, gbc);
		back.setForeground(Color.WHITE);
		back.setBackground(GameColors.GRIGIO_BARRA_LIVELLO);
		back.setName("Indietro");

		back.addActionListener(currentBackActionListener);
		gbc.gridx = 2;
		levelBar.add(back, gbc);
	}

	private ActionListener currentBackActionListener = new BackListener();
	private int correctCount;

	/**
	 * Incrementa il contatore delle risposte corrette. Ogni 2 invocazioni di
	 * questo metodo viene invocato il metodo incrementExperience di user che
	 * aumenta i punti esperienza associati allo utente corrente. Questo metodo
	 * restituisce un boolean che è true se l'esperienza aggiunta ha fatto
	 * incrementare il livello, false altrimenti. Se il livello è incrementato
	 * il contatore delle risposte corrette viene azzerato.
	 */
	public void correctAnswer() {
		correctCount++;
		boolean incrementLevel = false;
		if (correctCount % 2 == 0) {
			incrementLevel = currentUser.incrementExperience();
			panel.addExperience(); // quando aumenta l'esperienze dell'utente
									// viene aumentata anche la barra nella view
		}
		if (incrementLevel) { // livello utente aumentato
			correctCount = 0;
			panel.setLevel(currentUser.getLevel());
			panel.setLevelTarget(currentUser.getLevelTarget());
		}

	}

	public void prepareLevelBar() {
		currentLevel = currentUser.getLevel();
		if (panel != null) {
			levelBar.remove(panel);
			back.removeActionListener(currentBackActionListener);
		}
		panel = new ProgressBarPanel(currentUser.getExperience(), currentLevel);
		setLevelBar(currentUser.getUsername(), currentLevel, panel);

	}

	private class ProgressBarPanel extends JPanel {
		private int currentExp;
		private int nextLevel;
		private int currentLevel;
		private JLabel levelLabel = new JLabel();
		private GridBagConstraints gbc = new GridBagConstraints();
		private final static int LEVEL_X = 10;
		private final static int LEVEL_Y = 15;
		private final static int MAX_LEVEL = 280;
		private int levelTarget; // indica il numero di incrementi necessari a
									// riempire la barra del livello
		private int currentIncrement;

		public ProgressBarPanel(int currentExp, int currentLevel) {
			this.currentExp = currentExp;
			this.currentLevel = currentLevel;
			setLayout(new GridBagLayout());
			gbc.gridx = 0;
			gbc.gridy = 0;
			levelLabel.setForeground(Color.WHITE);
			levelLabel.setText("Livello " + currentLevel);
			setPreferredSize(new Dimension(300, 45));
			setBackground(GameColors.NERO_BARRA_SUPERIORE);
			add(levelLabel, gbc);
			if (currentLevel <= 1)
				setLevelTarget(10);
			else
				setLevelTarget(10 + (currentLevel - 1) * 5);

		}

		private void setLevelTarget(int levelTarget) {
			this.levelTarget = levelTarget;
			currentIncrement = MAX_LEVEL / levelTarget; // divido la dimensione
														// massima della barra
														// (in pixel) in
														// #levelTarget parti

		}

		public void paintComponent(Graphics g) {
			super.paintComponent(g);
			g.setColor(GameColors.GRIGIO_BARRA_LIVELLO);
			g.fillRect(LEVEL_X, LEVEL_Y, MAX_LEVEL, 15);
			g.setColor(GameColors.CELESTE);
			g.fillRect(LEVEL_X, LEVEL_Y, currentExp * currentIncrement, 15);
		}

		/**
		 * Aggiunge un punti esperienza. Se si arriva a fare #levelTarget punti
		 * di avanza di livello, aggiornando la view e il database.
		 */
		public void addExperience() {
			System.out.println("Esperienza corrente: " + currentExp);
			System.out.println("Esperienza utente: "
					+ currentUser.getExperience());
			// if(currentUser.getExperience()>currentExp){ //incremento la barra
			// solo se l'esperienza è aumentata anche in user
			currentExp++;
			// if(currentLevel < currentUser.getLevel()){ //livello aumentato
			// currentLevel = currentUser.getLevel();
			// resetLevel(currentLevel);
			// }
			repaint();
			// }
		}

		public void setLevel(int newLevel) {
			currentExp = 0;
			currentLevel = newLevel;
			levelLabel.setText("Level " + currentLevel);
			repaint();
		}
	}

	public State getState() {
		return state;
	}

	public User getUser() {
		return currentUser;
	}

	private class BackListener implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent a) {
			State currentState = state;
			State newState = currentState;
			System.out.println("CurrentState =" + currentState);
			if (currentState == State.RECOVERY_PASSWORD
					|| currentState == State.REGISTER) {
				newState = State.LOGIN;
			} else if (currentState == State.GAME_MENU
					|| currentState == State.CHRONOLOGY
					|| currentState == State.INSTRUCTIONS
					|| currentState == State.STATISTCS) {
				newState = State.MAIN_MENU;
			} else if (currentState == State.MAIN_MENU
					|| currentState == State.GAME) {
				newState = State.GAME_MENU;
			}
			startTransition(newState);
		}

	}

}
